/******************************************************************************
*  文 件 名：typeChange
*  功能描述：C语言版SDK 支持将xml报文及对应Restful接口地址输入，调用Restful接口
*  返回对应的json报文后，将其转变为对应的xml报文；同时提供对应测试用的main函数
*  函数：
*  json2xml ---json格式转换为xml格式
*  外部依赖文件：cJSON.c  cJson.h  cJSON.h  cJSON_Utils.c  cJSON_Utils.h
*  外部依赖库：libxml2 库
*  开发历史：
*  1.0         2019年5月28日    --版本建立
*  1.1         2019年5月28日    --新增json转xml函数
*  1.2         2019年5月29日    --新增xml转json函数
*  1.3         2019年5月30日    --新增socket链接函数
*  作者： Yuxz（Fish Paradise）
******************************************************************************/

#include <libxml2/libxml/parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>


/******************************************************************************
*  函 数 名：json2xml
*  功能描述：json格式转换为xml格式
*  入参：strJson  ---Json格式的char*类型字符串
*  返回值：XML格式的char*类型字符串
*  开发历史：
*  1.0         2019年5月28日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
char *json2xml( char *strJson );


/******************************************************************************
*  函 数 名：toResultXml
*  功能描述：json格式转换为xml格式
*  入参：strJson  ---Json格式的char*类型字符串
*  返回值：封装好的XML格式字符串（在json2xml基础上封装了xml头部及一些格式修改）
*  开发历史：
*  1.0         2019年5月28日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
char* toResultXml( char* strJson );


/******************************************************************************
*  函 数 名：xml2Json
*  功能描述：xml格式字符串转换为json格式字符串
*  入参：strXml  ---xml格式的char*类型字符串
*  返回值：json格式的char*类型字符串
*  开发历史：
*  1.0         2019年5月29日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
char* xml2Json( char *strXml );


/******************************************************************************
*  函 数 名：ParseXML2Json
*  功能描述：xml格式结构体转换为json格式字符串
*  入参：cur  ---xmlNodePtr 格式结构体，xml文档解析后的根节点
*  outJsonChar --- 用指针形式带出新返回值，方便循环调用
*  返回值：无
*  开发历史：
*  1.0         2019年5月29日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
void ParseXML2Json( xmlNodePtr cur, char *outJsonChar );


/******************************************************************************
*  函 数 名：connectRestful
*  功能描述：链接restful接口，执行主要SDK方法
*  入参：ip  ---链接的IP地址
*  port --- Restful接口提供的端口号
*  jsonStr --- 传入的json报文
*  返回值：无
*  开发历史：
*  1.0         2019年5月30日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
char* connectRestful( const char * ip, const char *port, const char * jsonStr );


/******************************************************************************
*  函 数 名：process_info
*  功能描述：向创建好的socket TCP句柄发送格式化http报文
*  入参：fd  ---创建好的socket TCP句柄
*  sendJson --- 发送的json报文
*  返回值：无
*  开发历史：
*  1.0         2019年5月30日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
char* process_info( int fd, const char *sendJson );


/******************************************************************************
*  函 数 名：splitChar
*  功能描述：将返回的socket TCP内容解析为可以阅读的xml报文
*  入参：spstr  ---返回的socket TCP内容
*  返回值：解析好的XML格式的char *字符串
*  开发历史：
*  1.0         2019年5月30日    --函数建立
*  作者： Yuxz（Fish Paradise）
******************************************************************************/
char* splitChar( char* spstr );


/* 日志级别标志 */
char* controlflag;

int main( int argc, char **argv )
{
	/**
	 *	main函数参数定义如下（编译完成后）：
	 *	执行方法 参数1  参数2 参数3
	 *	参数1：0 无日志
	 *		   1 只打出connectRestful方法日志
	 *		   2 打出全部日志
	 *		   此时 参数2 为Restful IP地址
	 *			 参数3 为Restful 端口
	 *			 默认读取本地 test.xml文件进行测试
	 *		   3 测试xml2Json 函数
	 *			此时 参数2 如果为空，则默认读取本地test.xml文件,
	 *			并将转换好的json字符串输出到标准输出，参数2如果不为空，
	 *			则读取参数2的文件进行测试输出
	 *		   4 测试json2xml 函数
	 *			此时 参数2 如果为空，则默认读取本地1.json文件,
	 *			并将转换好的xml字符串输出到标准输出，参数2如果不为空，
	 *			则读取参数2的文件进行测试输出
	 *
	 **/
	controlflag = argv[1];

	/**
	 *  xml转json 测试代码，读取本地test.xml文件，然后调用转换函数转换后，
	 *  将转换后的json输出到标准输出中
	 **/
	FILE *fp1 = NULL;
	if ( !strcmp( controlflag, "3" ) && !(argv[2] == NULL) )
	{
		if ( (fp1 = fopen( argv[2], "r" ) ) == NULL )
		{
			perror( "open file failed !\n" );
			exit( 1 );
		}
	}else
	if ( (fp1 = fopen( "./test.xml", "r" ) ) == NULL )
	{
		perror( "open test.xml failed !\n" );
		exit( 1 );
	}
	char	xml2JsonPart[10240]	= { 0 };
	char	Line2[10240]		= { 0 }, jsonChar2[10240] = { 0 };
	int	iTranDataLen		= sizeof(jsonChar2);

	while ( fgets( Line2, 255, fp1 ) )
	{
		strcat( jsonChar2, Line2 );
		memset( Line2, 0, sizeof(Line2) );
	}

	const char* jsonRs = xml2Json( jsonChar2 );

	if ( !strcmp( controlflag, "3" ) )
	{
		printf( "JSON : \n%s\n", jsonRs );
		return(0);
	}


	/**
	 *	json转xml 测试代码结束
	 *
	 **/


	/**
	 *  json转xml 测试代码，读取本地1.json文件，然后调用转换函数转换后，
	 *  将转换后的xml输出到标准输出中
	 **/
	if ( !strcmp( controlflag, "4" ) )
	{
		FILE *fp = NULL;
		if ( argv[2] == NULL )
		{
			if ( (fp = fopen( "./1.json", "r" ) ) == NULL )
			{
				perror( "open 1.json failed !\n" );
				exit( 1 );
			}
		} else{
			if ( (fp = fopen( argv[2], "r" ) ) == NULL )
			{
				perror( "open file failed !\n" );
				exit( 1 );
			}
		}
		const char	* xmlChar	= NULL;
		char		Line[1024]	= { 0 }, jsonChar[10240] = { 0 };
		while ( fgets( Line, 255, fp ) )
		{
			strcat( jsonChar, Line );
			memset( Line, 0, sizeof(Line) );
		}
		xmlChar = toResultXml( jsonChar );

		printf( "XML : \n%s\n", xmlChar );
		return(0);
	}


	/**
	 *	json转xml 测试代码结束
	 *
	 **/
	connectRestful( argv[2], argv[3], jsonRs );
	return(0);
}


char* json2xml( char *strJson )
{
	char	strXml[40960]		= "\0";
	char	strSubKey[10240]	= "\0";
	char	strSubValue[10240]	= "\0";
	char	strSubXml[10240]	= "\0";
	char	strKey[10240]		= "\0";
	char	strTemp[10240]		= "\0";
	char	strVal[10240]		= "\0";

	cJSON *pRoot = cJSON_Parse( strJson );
	if ( NULL == pRoot )
	{
		perror( "empty Json String" );
		exit( -1 );
	}
	cJSON *pChild = pRoot->child;
	while ( pChild != NULL )
	{
		if ( pChild->child != NULL ) /* 存在子节点的情况 */
		{
			memset( strSubKey, 0, sizeof(strSubKey) );
			memset( strSubValue, 0, sizeof(strSubValue) );
			memset( strSubXml, 0, sizeof(strSubXml) );
			memset( strTemp, 0, sizeof(strTemp) );
			snprintf( strSubKey, sizeof(strSubKey), "%s", pChild->string );
			snprintf( strTemp, sizeof(strTemp), "%s", cJSON_Print( pChild ) );
			strcat( strSubValue, json2xml( strTemp ) );
			if ( strcmp( strSubKey, "(null)" ) )
			{
				if ( !strstr( strSubKey, "ARRAY" ) && !strstr( strSubKey, "Array" ) )
				{
					snprintf( strSubXml, sizeof(strSubXml), "<%s>%s</%s>", strSubKey, strSubValue, strSubKey );
				}else {
					snprintf( strSubXml, sizeof(strSubXml), "<%s><Row>%s</Row></%s>", strSubKey, strSubValue, strSubKey );
				}
			}else {
				snprintf( strSubXml, sizeof(strSubXml), "%s", strSubValue );
			}
			strcat( strXml, strSubXml );
		}else {
			memset( strKey, 0, sizeof(strKey) );
			memset( strTemp, 0, sizeof(strTemp) );
			memset( strVal, 0, sizeof(strVal) );

			snprintf( strKey, sizeof(strKey), "%s", pChild->string );
			if ( pChild->valuestring != NULL )
			{
				snprintf( strVal, sizeof(strVal), "%s", pChild->valuestring );
			}else {
				snprintf( strVal, sizeof(strVal), "%s", cJSON_Print( pChild ) );
			}
			snprintf( strTemp, sizeof(strTemp), "<%s>%s</%s>", strKey, strVal, strKey );

			strcat( strXml, strTemp );
		}

		pChild = pChild->next;
	}
	if ( NULL != pRoot )
	{
		cJSON_Delete( pRoot );
	}

	char * outXml = (char *) malloc( strlen( strXml ) * sizeof(char) );
	snprintf( outXml, strlen( strXml ) + 1, "%s", strXml );
	return(outXml);
}


char* toResultXml( char* strJson )
{
	/* 拼接xml */
	const char	* xmlHead	= "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Reply_Msg>";
	const char	* xmlTail	= "</Reply_Msg>";
	const char	* xmlChar	= json2xml( strJson );
	char		* xmlResult	= (char *) malloc( strlen( xmlHead ) + strlen( xmlTail ) + strlen( xmlChar ) + 1 );
	int		flag		= sprintf( xmlResult, "%s%s%s", xmlHead, xmlChar, xmlTail );
	return(xmlResult);
}


char* xml2Json( char *strXml )
{
	xmlDocPtr	doc	= NULL; /* 定义解析文档指针 */
	xmlNodePtr	curNode = NULL; /* 定义结点指针 */

	char	typeChangetmp[20480]	= "\0";
	int	xmlSize			= strlen( strXml );


	xmlKeepBlanksDefault( 0 );
	doc = xmlParseMemory( strXml, xmlSize );
	if ( NULL == doc )
	{
		perror( "Document not parsed successfully. \n" );
		exit( 1 );
	}

	curNode = xmlDocGetRootElement( doc ); /* 确定文档根元素 */
	if ( NULL == curNode )
	{
		perror( "empty document\n" );
		xmlFreeDoc( doc );
		exit( 1 );
	}

	ParseXML2Json( curNode, typeChangetmp );
	static char	jsonResult[20480]	= "\0";
	char		jsontmp[20480]		= "\0";
	strncpy( jsontmp, typeChangetmp + 10, strlen( typeChangetmp ) - 13 );
	snprintf( jsonResult, sizeof(jsonResult), "%s}", jsontmp );
	xmlFreeDoc( doc );
	xmlCleanupParser();
	xmlMemoryDump();
	return(jsonResult);
}


void ParseXML2Json( xmlNodePtr cur, char *outJsonChar )
{
	char	xml2JsonPart[10240]	= "\0"; /* XML转JSON片段 */
	char	element[10240]		= "\0"; /* 组成xml的元素 */


	while ( cur != NULL )
	{
		memset( xml2JsonPart, 0, sizeof(xml2JsonPart) );
		memset( element, 0, sizeof(element) );

		if ( !strcmp( cur->name, "Row" ) )
		{
			cur = cur->xmlChildrenNode;
		}

		if ( cur->xmlChildrenNode != NULL && cur->xmlChildrenNode->type != XML_TEXT_NODE )
		{
			memset( element, 0, sizeof(element) );
			ParseXML2Json( cur->xmlChildrenNode, xml2JsonPart );

			if ( !strstr( cur->name, "ARRAY" ) )
			{
				snprintf( element, sizeof(element), "\"%s\":{%s},", cur->name, xml2JsonPart );
			}else {
				snprintf( element, sizeof(element), "\"%s\":[{%s}],", cur->name, xml2JsonPart );
			}

			strcat( outJsonChar, element );
		}else {
			if ( cur->xmlChildrenNode != NULL )
			{
				if ( cur->next != NULL )
				{
					snprintf( xml2JsonPart, sizeof(xml2JsonPart), "\"%s\":\"%s\",",
						  cur->name, cur->xmlChildrenNode->content );
				}else {
					snprintf( xml2JsonPart, sizeof(xml2JsonPart), "\"%s\":\"%s\"",
						  cur->name, cur->xmlChildrenNode->content );
				}
			}else {
				if ( cur->next != NULL )
				{
					snprintf( xml2JsonPart, sizeof(xml2JsonPart), "\"%s\":\"\",", cur->name );
				}else {
					snprintf( xml2JsonPart, sizeof(xml2JsonPart), "\"%s\":\"\"", cur->name );
				}
			}
			snprintf( element, sizeof(element), "%s", xml2JsonPart );
			strcat( outJsonChar, element );
		}
		cur = cur->next;
	}
}


char* connectRestful( const char * ip, const char *port, const char * jsonStr )
{
	int			sockfd, numbytes;
	char			buf[BUFSIZ];
	struct sockaddr_in	their_addr;
	if ( !(strcmp( controlflag, "2" ) && strcmp( controlflag, "1" ) ) )
	{
		printf( "prepare SOCKET ...\n" );
	}
	while ( (sockfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
		;
	if ( !(strcmp( controlflag, "2" ) && strcmp( controlflag, "1" ) ) )
	{
		printf( "start connet to %s:%s\n", ip, port );
	}
	their_addr.sin_family		= AF_INET;
	their_addr.sin_port		= htons( atoi( port ) );
	their_addr.sin_addr.s_addr	= inet_addr( ip );
	bzero( &(their_addr.sin_zero), 8 );

	while ( connect( sockfd, (struct sockaddr *) &their_addr, sizeof(struct sockaddr) ) == -1 )
		;
	if ( !(strcmp( controlflag, "2" ) && strcmp( controlflag, "1" ) ) )
	{
		printf( "connected...\n prepare to send http message\n" );
	}
	char* recivedtoxml = process_info( sockfd, jsonStr );
	if ( !(strcmp( controlflag, "2" ) && strcmp( controlflag, "1" ) ) )
	{
		printf( "recived and transform to xml:\n%s\n", recivedtoxml );
	}
	close( sockfd );
	return(recivedtoxml);
}


char* splitChar( char* spstr )
{
	char	*ptr;
	char	*p;
	if ( !(strcmp( controlflag, "2" ) ) )
	{
		printf( "before split:  str=\n%s\n", spstr );
		printf( "start to split:\n" );
	}

	ptr = strtok_r( spstr, "\r\n", &p );
	char q[20480] = { 0 };
	while ( ptr != 0 )
	{
		if ( strstr( ptr, "{\"" ) )
		{
			strcpy( q, ptr );
		}
		ptr = strtok_r( NULL, "\r\n", &p );
	}
	char * m = toResultXml( q );
	return(m);
}


char* process_info( int fd, const char *sendJson )
{
	int	send_num;
	char	send_buf[] = "helloworld";
	char	recv_buf [4096];
	char	str1[40960];
	if ( !(strcmp( controlflag, "2" ) ) )
	{
		printf( "begin send http message\n" );
	}

	memset( str1, 0, 4096 );
	strcat( str1, "POST /test HTTP/1.1\r\n" );
	strcat( str1, "Host: 192.168.189.129:8080\r\n" );
	char str3[20];
	snprintf( str3, 25, "Content-Length: %ld\r\n", strlen( sendJson ) + 10 );
	strcat( str1, str3 );
	strcat( str1, "Content-Type: application/json\r\n" );
	strcat( str1, "\r\n" );
	strcat( str1, sendJson );
	strcat( str1, "\r\n\r\n" );
	if ( !(strcmp( controlflag, "2" ) ) )
	{
		printf( "http message = %s\n", str1 );
	}
	send_num = send( fd, str1, strlen( str1 ), 0 );
	if ( send_num < 0 )
	{
		perror( "send error" );
		exit( 1 );
	}else {
		if ( !(strcmp( controlflag, "2" ) ) )
		{
			printf( "send successful\n" );
			printf( "begin recive:\n" );
		}

		int recv_num = recv( fd, recv_buf, sizeof(recv_buf), 0 );
		if ( recv_num < 0 )
		{
			perror( "recived error" );
			exit( 1 );
		} else {
			if ( !(strcmp( controlflag, "2" ) ) )
			{
				printf( "recived sucess:\n%s\n", recv_buf );
				printf( "begin to split recived message..." );
			}

			return(splitChar( recv_buf ) );
		}
	}
}